
#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Net;
using System.IO;
using Microsoft.Xna.Framework.GamerServices;
#endregion


namespace Pauliver
{
    public class NetworkGameManagerTeamed : GameManager, GameManagerTeamedInterface
    {
        bool Ended = false;
        public const short REMOVEOBJECT = 0x80;
        public const short GAMEOVER = 0x81;
        public const short READTIME = 0x82;
        public const short PUSHINPUT = 0x83;
        public const short JIGGLESHOT = 0x84;
        
        //The PlayerTeamManager and the UserTeamManager
        //Should be rolled together into one class
        //With a "Network" option
        //protected PlayerTeamManager po;
        //protected UserTeamManagement<PlayerIndex> pi;

        protected UserTeamPlayerManager UTPM;

        protected int playercount = 0;
        protected int TeamCount;
        protected const int SL = 30;

        protected double TotalSeconds;
        protected double TurnStart;
        double TurnTime;

        protected AI ai;

        protected String TestMessage;

        public bool m_ChangeTeam = true;

        protected NetworkManager NetManager;

        public UserTeamPlayer CurrentUTP()
        {
            return UTPM.Current;
        }

        public bool CanChangeTeam()
        {
            return m_ChangeTeam;
        }

        public void SetCanChangeTeam(bool value)
        {
            m_ChangeTeam = value;
        }

        public bool SetHitpoints(PacketReader pr)
        {
            int id = pr.ReadInt32();
            int hitpoints = pr.ReadInt32();
            ((_SceneGraph[id])[ComponentType.Game_Hitpoint] as Game_Hitpoints).NetworkSetHitpoints( hitpoints );
            return true;
        }

        public bool RemoveObject(PacketReader pr)
        {
            int id = pr.ReadInt32();
            this.RemoveFromSceneGraph( (_SceneGraph[id]) );
            return true;
        }

        public bool GameOver(PacketReader pr)
        {
            if (!Ended)
            {
                Ended = true;
                Team team = (Team)pr.ReadInt32();
                Game1.Instance.AddMenu(new GameOver(team, true));
                Game1.Instance.EndGame();
            }
            return true;
        }


        public NetworkGameManagerTeamed(List<Triplet<Team, PlayerIndex, TeamType>> teams, DynamicLevel level, BaseCollision Collision, NetworkManager nm)
            : base(level, Collision)
        {
            Game1.Instance.PopTopMenu();
            NetManager = nm;

            //NetManager.AddNCallBack("SwitchTurn", SwitchTurn);
            NetManager.AddNCallBack(Game_Hitpoints.SETHITPOINTS, SetHitpoints);
            NetManager.AddNCallBack(READTIME, ReadTime);
            NetManager.AddNCallBack(PUSHINPUT, PushInput);
            NetManager.AddNCallBack(REMOVEOBJECT, RemoveObject);
            NetManager.AddNCallBack(GAMEOVER, GameOver);
            NetManager.AddPlayerLeftCallback(PlayerLeft);
            NetManager.AddNetworkSessionEndedCallback(GameEnded);
           
            TeamCount = teams.Count;
            playercount = 3 * teams.Count;

            UTPM = new UserTeamPlayerManager();

            for (int i = 0; i < teams.Count; ++i)
            {
                UTPM.AddTeam(teams[i].First,teams[i].Third);
                UTPM.AddPlayerToTeam(teams[i].First, teams[i].Second);
            }
            TurnStart = Game1.Instance.SecondsSoFar;
            SetupPlayers();

            TestMessage = "Welcome";

            EventManager.GetInstance().AddEvent(ClearText, 5);
            
            ((UTPM.CurrentAvatar[ComponentType.Holder_Weapon] as Holder_Weapon).Weapon[ComponentType.Input_Shoot] as Input_Shoot).CanShoot = true;

            ai = null;
        }

        void PlayerLeft(NetworkGamer gamer)
        {
            if (NetManager.IsHost())
            {
                if (gamer.Tag != null)
                {
                    Team temp = (Team) ((int)gamer.Tag); //.Value;
                    PlayerIndex? pi = UTPM.SwapTeamType(temp, TeamType.AI);
                    if(pi != null)
                        ai = new AI((FakeGamePad)PInput.Instance.GamePad( (PlayerIndex) pi), UTPM);
                }
            }
        }

        public virtual void SetupPlayers()
        {
            //base.SetupPlayers();
            int spacing = (int) (Game1.Instance.World.Y - (Terrain.tolerance * 2)) / playercount;
            List<Team> teams = UTPM.GetTeams();
            int CurrentIndex = 0;
            for (int i = 0; i < playercount; i++)
            {
                PlayerObject poi = new PlayerObject();
                (poi[ComponentType.Game_Position] as Game_Position).X += Terrain.tolerance + (i*2) * spacing;
                (poi[ComponentType.Game_Position] as Game_Position).Y = (Level as DynamicLevel)[(int)(poi[ComponentType.Game_Position] as Game_Position).X] - 16;

                poi.AddComponent(new Game_Team(teams[CurrentIndex]));
                PlayerHitpoints ph = new PlayerHitpoints(poi);
                UTPM.AddAvatarToTeam(teams[CurrentIndex], poi);

                base._SceneGraph.Add(poi);
                base._SceneGraph.Add(ph);
                
                if (++CurrentIndex >= teams.Count)
                {
                    CurrentIndex = 0;
                }
            }

            //po.Setup();
            //pi.Setup();
            UTPM.Setup();
            Game1.Instance.CenterOn((UTPM.CurrentAvatar[ComponentType.Game_Position] as Game_Position).Position);
        }

        public virtual void ProcessInput(PlayerObject Current, PlayerIndex controller)
        {
            UTPM.CurrentAvatar.ProcessInput(UTPM.CurrentPlayer);
        }


        public void GameEnded(String reason)
        {
            if(!Ended)
            {
                TestMessage = reason;
                EventManager.GetInstance().AddEvent(GameEnded, 10);
            }
        }

        public void GameEnded()
        {
            if (!Ended)
            {
                Ended = true;
                Game1.Instance.EndGame();
                Game1.Instance.AddMenu(new MainMenu());
            }
        }
        /*
        public bool SwitchTurn(PacketReader pr)
        {
            SwitchTeam(TotalSeconds, true);
            temp = 0.0f;
            return true;
        }
        */

        public bool ReadTime(PacketReader pr)
        {
            TotalSeconds = pr.ReadDouble();
            TurnStart = pr.ReadDouble();
            TurnTime = pr.ReadDouble();
            return true;
        }

        public bool PushInput(PacketReader pr)
        {
            PInput.Instance.ReadInputFromNetwork(UTPM.CurrentPlayer, pr);
            return true;
        }


        bool InputComesFromMyMachine = false;

        public bool MyMachineIsActive()
        {
            return InputComesFromMyMachine;
        }

        public override void Update(GameTime p_time)
        {
            InputComesFromMyMachine = (UTPM.Current.TeamType == TeamType.Local);

            if ( InputComesFromMyMachine || (NetManager.IsHost() && UTPM.Current.TeamType == TeamType.AI) )
            {
                TotalSeconds = p_time.TotalGameTime.TotalSeconds;
                TurnTime = TotalSeconds - (double)TurnStart;

                {
                    NetManager.SendToAll(NetworkManager.STARTTURN);
                }

                {
                    PacketWriter pw = new PacketWriter();
                    pw.Write(READTIME);
                    pw.Write(TotalSeconds);
                    pw.Write(TurnStart);
                    pw.Write(TurnTime);
                    NetManager.SendToAll(pw);
                }
                {
                    PacketWriter pw = new PacketWriter();
                    pw.Write(PUSHINPUT);
                    PInput.Instance.WriteInputToNetwork(UTPM.CurrentPlayer, pw);
                    NetManager.SendToAll(pw);
                }

                if (NetManager.IsHost() && UTPM.Current.TeamType == TeamType.AI)
                {
                    InputComesFromMyMachine = true;
                    if (UTPM.Current.TeamType == TeamType.AI)
                    {
                        if (ai != null)
                        {
                            ai.Update((float)TurnTime);
                        }
                        else
                        {   //we can now not worry about if players drop and become AI since they will be created here
                            ai = new AI((FakeGamePad)PInput.Instance.GamePad(UTPM.CurrentPlayer), UTPM);
                        }
                    }
                    {
                        PacketWriter pw = new PacketWriter();
                        pw.Write(PUSHINPUT);
                        PInput.Instance.WriteInputToNetwork(UTPM.CurrentPlayer, pw);
                        NetManager.SendToAll(pw);
                    }
                }
            }
            else if (!NetManager.HasDataThisUpdate())
            {
                // this call is not needed //NetManager.SendToAll(NetworkManager.IGNORE);
                return;    
            }

            if (UTPM.GetWinningTeam() != Team.NoTeam)
            {
                if (NetManager.IsHost())
                {
                    int winningteam = (int) UTPM.GetWinningTeam();
                    PacketWriter pw = new PacketWriter();
                    pw.Write(GAMEOVER);
                    pw.Write(winningteam);
                    NetManager.SendToAll(pw);

                    Game1.Instance.AddMenu( new GameOver( (Team)winningteam, true) );
                    Game1.Instance.EndGame();
                    NetManager.EndGame();
                    NetManager.Update();
                    System.Threading.Thread.Sleep(10);
                    NetManager.Update();
                    System.Threading.Thread.Sleep(10);
                    NetManager.Update();
                    System.Threading.Thread.Sleep(10);
                    NetManager.Update();
                    System.Threading.Thread.Sleep(10);
                    NetManager.Update();
                    System.Threading.Thread.Sleep(10);
                    NetManager.Disconnect();
                    return;
                }
            }

            if ((TurnTime >= SL) && m_ChangeTeam)
            {
                SwitchTeam(TotalSeconds, true);
                TurnTime = 0.0f;
            }

            if (m_ChangeTeam)
            {
                if (PInput.Instance.GamePad(UTPM.CurrentPlayer).Back.State == InputWrapper.ButtonState.Pressed)
                {//Advance to Team and Next Player   
                    SwitchTeam(TotalSeconds, true);
                }
            }

            if (Game1.Instance.CurrentMenu == null)
            {
                ProcessInput(UTPM.CurrentAvatar, UTPM.CurrentPlayer);
            }
            else
            {
                Game1.Instance.CurrentMenu.ProccessInput(UTPM.CurrentPlayer);
            }
            if ((UTPM.CurrentAvatar[ComponentType.Collidable] as Collidable).Moving)
            {
                Game1.Instance.CenterOn((UTPM.CurrentAvatar[ComponentType.Game_Position] as Game_Position).Position);
            }
            base.Update(p_time);
  
            if (InputComesFromMyMachine)
            {
                NetManager.SendToAll(NetworkManager.ENDTURN);
            }
        }

        public virtual void FakeSwitchTeam2(double p_time, bool CanShoot)
        {
            (UTPM.CurrentAvatar[ComponentType.Drawable_Sprite_Animated] as Drawable_Sprite_Animated).SetCurrentAnimation("Idle" + UTPM.CurrentAvatar.GetFacing());
            TurnStart = Game1.Instance.SecondsSoFar;
            // Dont' advance, we did this allready //UTPM.AdvancePlayerOnly();
            Holder_Weapon hw = (UTPM.CurrentAvatar[ComponentType.Holder_Weapon] as Holder_Weapon);
            if (hw != null)
            {
                (hw.Weapon[ComponentType.Input_Shoot] as Input_Shoot).CanShoot = CanShoot;
            }
            if (Game1.Instance.CurrentMenu != null)
            {
                Game1.Instance.PopTopMenu();
            }
            Game1.Instance.SlideTowards((UTPM.CurrentAvatar[ComponentType.Game_Position] as Game_Position).Position);

            if (UTPM.Current.TeamType == TeamType.AI)
            {
                ai = new AI((FakeGamePad)PInput.Instance.GamePad(UTPM.CurrentPlayer), UTPM);
            }
            else
            {
                ai = null;
            }

            TestMessage = UTPM.CurrentTeam + "'s Turn";
            EventManager.GetInstance().AddEvent(ClearText, 5);
        }
        public virtual void FakeSwitchTeam(double p_time,bool CanShoot)
        {
            (UTPM.CurrentAvatar[ComponentType.Drawable_Sprite_Animated] as Drawable_Sprite_Animated).SetCurrentAnimation("Idle" + UTPM.CurrentAvatar.GetFacing());
            TurnStart = Game1.Instance.SecondsSoFar; // Not p_time since p_time was passed in possibly from the network..
            UTPM.AdvancePlayerOnly();
            Holder_Weapon hw = (UTPM.CurrentAvatar[ComponentType.Holder_Weapon] as Holder_Weapon);
            if (hw != null)
            {
                (hw.Weapon[ComponentType.Input_Shoot] as Input_Shoot).CanShoot = CanShoot;
            }
            if(Game1.Instance.CurrentMenu != null)
            {
                Game1.Instance.PopTopMenu();
            }
            Game1.Instance.SlideTowards((UTPM.CurrentAvatar[ComponentType.Game_Position] as Game_Position).Position);

            if ( UTPM.Current.TeamType == TeamType.AI && NetManager.IsHost() )
            {
                ai = new AI((FakeGamePad)PInput.Instance.GamePad(UTPM.CurrentPlayer),UTPM);
            }
            else
            {
                ai = null;
            }

            TestMessage = TeamManager.Instance.GetTeamString(UTPM.CurrentTeam) + "'s Turn";
            EventManager.GetInstance().AddEvent(ClearText, 5);
        }
        
        public virtual void SwitchTeam(double p_time,bool CanShoot)
        {
            (UTPM.CurrentAvatar[ComponentType.Drawable_Sprite_Animated] as Drawable_Sprite_Animated).SetCurrentAnimation("Idle" + UTPM.CurrentAvatar.GetFacing());
            TurnStart = Game1.Instance.SecondsSoFar; // Not p_time since p_time was passed in possibly from the network..
            UTPM.Advance();
            Holder_Weapon hw = (UTPM.CurrentAvatar[ComponentType.Holder_Weapon] as Holder_Weapon);
            if (hw != null)
            {
                (hw.Weapon[ComponentType.Input_Shoot] as Input_Shoot).CanShoot = CanShoot;
            }
            if(Game1.Instance.CurrentMenu != null)
            {
                Game1.Instance.PopTopMenu();
            }
            Game1.Instance.SlideTowards((UTPM.CurrentAvatar[ComponentType.Game_Position] as Game_Position).Position);

            if ( UTPM.Current.TeamType == TeamType.AI && NetManager.IsHost() )
            {
                ai = new AI((FakeGamePad)PInput.Instance.GamePad(UTPM.CurrentPlayer),UTPM);
            }
            else
            {
                ai = null;
            }

            TestMessage = TeamManager.Instance.GetTeamString(UTPM.CurrentTeam) + "'s Turn";
            EventManager.GetInstance().AddEvent(ClearText, 5);
        }

        public override void Draw(SpriteBatch p_SpriteBatch, GameTime p_GameTime, Rectangle p_Screen)
        {
            SpriteFont timefont = PContentManager.Instance.GetObject<SpriteFont>(@"Content/Art/Fonts/TimeFont");
            SpriteFont invader = PContentManager.Instance.GetObject<SpriteFont>(@"Content/Art/Fonts/Irken");
            Texture2D target = PContentManager.Instance.GetObject<Texture2D>(@"Content/Art/Sprites/Reticle");
            
            base.Draw(p_SpriteBatch, p_GameTime, p_Screen);


            if (UTPM.TotalPlayerCount() > 0)
            {
                p_SpriteBatch.DrawString(timefont, ((double)SL - (TurnTime)).ToString("##"), new Vector2(110, 10), TeamManager.Instance.GetTeam(UTPM.CurrentTeam), 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, DrawLayer.GetLayer(DrawLayer.LayerDepth.ScreenText));
            }

            p_SpriteBatch.DrawString(invader, TestMessage, new Vector2(55, 225), Color.White, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, DrawLayer.GetLayer(DrawLayer.LayerDepth.ScreenText) );

            if (!m_ChangeTeam && Game1.Instance.CurrentMenu == null)
            {
                Input_Shoot isc = (UTPM.CurrentAvatar[ComponentType.Holder_Weapon] as Holder_Weapon).Weapon[ComponentType.Input_Shoot] as Input_Shoot;
                if (isc != null && isc.FiringPower != 0)
                {
                    float scale = 1 + 2 * isc.FiringPower / 45;
                    int x, y;
                    x = (int)(Game1.Instance.CurrentViewport.Width / 2);
                    y = (int)(Game1.Instance.CurrentViewport.Height / 2);
                    Vector2 origin = new Vector2(target.Height / 2, target.Width / 2);
                    p_SpriteBatch.Draw(target, new Vector2(x, y), null, Color.White, 0.0f, origin, scale, SpriteEffects.None, DrawLayer.GetLayer(DrawLayer.LayerDepth.ArrowAbovePlayer) );
                }
            }
        }

        public PlayerIndex CurrentPlayer()
        {
            return UTPM.CurrentPlayer;
        }

        public void ClearText()
        {
            TestMessage = "";
        }

        protected override void RemoveObjects()
        {
            bool switch_teams = false;
            if (_RemoveUs.Contains(UTPM.CurrentAvatar))
            {
                switch_teams = true;
            }
            foreach(GameObject po in _RemoveUs)
            {
                if(po is PlayerObject)
                {
                    bool switched_teams = UTPM.RemoveAvatar(po as PlayerObject);
                    if (switched_teams)
                    {
                        FakeSwitchTeam2(TotalSeconds, true);
                        switch_teams = false;
                    }
                }
            }
            base.RemoveObjects();

            if (switch_teams && UTPM.PlayerManagers.Count > 0)
            {
                FakeSwitchTeam(TotalSeconds, true);
            }
            if(UTPM.PlayerManagers.Count == 0)
            {
                Game1.Instance.AddMenu(new GameOver(Team.NoOneLeft, true));
                Game1.Instance.EndGame();
            }
        }
    }

}
